package com.zjc.gateway.filter;

import com.google.gson.Gson;
import com.zjc.gateway.constants.Constant;
import com.zjc.gateway.redis.RedisUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 服务网关（过滤器）：只需要继承ZuulFilter抽象类 http://localhost:8101/test/dc?accessToken=token
 * <p>
 * 没有过滤时： 所有请求都会被毫无保留地转发到具体的应用并返回结果
 * <p>
 * 解决的问题： 前置网关过滤或拦截器，来完成与具体业务无关的：灰度发布、请求限流、签名校验、权限校验等功能
 */
@Component
public class AccessFilter implements GlobalFilter, Ordered {

    private static final Logger logger = LoggerFactory.getLogger(AccessFilter.class);

    private Gson gson = new Gson();

    @Autowired
    RedisUtils<String> redisUtil;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        String uri = request.getURI().getPath();
        logger.info("requst url:" + uri);

        if (!uri.equals(Constant.LOGIN_WHITELIST)
                && !uri.equals(Constant.LOGIN_MIN_APP_WHITELIST)
                && !uri.contains(Constant.VERIFICATION_CODE_SEND)
                && !uri.startsWith(Constant.LOGIN_WITH_TOKEN_WHITELIST)
                && !uri.startsWith(Constant.OPEN_API)) {
            String token = this.resolveToken(request);
            if (!StringUtils.hasText(token)) {
                return handleSignFailure(response);
            }

            String sessionJson = redisUtil.get(Constant.SESSION_PREFIX_USER + token);
            if (!StringUtils.hasText(sessionJson)) {
                return this.handleSessionTimeout(response);
            }
            redisUtil.expire(Constant.SESSION_PREFIX_USER + token, Constant.SESSION_EXPIRE_TIME_USER * 60);
            try {
                sessionJson = URLEncoder.encode(sessionJson, "utf-8");
            } catch (UnsupportedEncodingException e) {
                logger.error("登录参数获取失败: " + e.getMessage() + e.getCause());
            }
            request = request.mutate().header(Constant.RESTAPI_HEADER_SESSION_KEY, sessionJson).build();
            //把新的 exchange放回到过滤链
            return chain.filter(exchange.mutate().request(request).build());

        }
        return chain.filter(exchange);
    }

    private Mono<Void> handleSignFailure(ServerHttpResponse response) {
        logger.error("token not exist");
        Map<String, String> map = new HashMap<>(3);
        map.put("code", "GW.361");
        map.put("msg", "fail,token not exist");
        String data = gson.toJson(map);
        DataBuffer wrap = null;
        try {
            wrap = response.bufferFactory().wrap(data.getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            logger.error("handleSignFailure 失败: " + e.getMessage() + e.getCause());
        }
        return response.writeWith(Mono.just(wrap));
    }

    private Mono<Void> handleSessionTimeout(ServerHttpResponse response) {
        Map<String, String> map = new HashMap<>(3);
        map.put("code", "GW.360");
        map.put("msg", "用户登录已过期，请重新登录");
        String data = gson.toJson(map);
        DataBuffer wrap = null;
        try {
            wrap = response.bufferFactory().wrap(data.getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            logger.error("handleSessionTimeout 失败: " + e.getMessage() + e.getCause());
        }
        return response.writeWith(Mono.just(wrap));
    }


    private String resolveToken(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        List<String> sessionId = headers.get("sessionId");
        if (null == sessionId || sessionId.size() == 0) {
            return null;
        }
        return sessionId.get(0);
    }

    public static void main(String[] args) {
        System.out.println("/ea/ss/d".contains("/ea/"));
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
